{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# ⚓️ Retrieval with FastEmbed\n",
    "\n",
    "This notebook demonstrates how to use FastEmbed to perform vector search and retrieval. It consists of the following sections:\n",
    "\n",
    "1. Setup: Installing the necessary packages.\n",
    "2. Importing Libraries: Importing FastEmbed and other libraries.\n",
    "3. Data Preparation: Example data and embedding generation.\n",
    "4. Querying: Defining a function to search documents based on a query.\n",
    "5. Running Queries: Running example queries.\n",
    "\n",
    "## Setup\n",
    "\n",
    "First, we need to install the dependencies. `fastembed` to create embeddings and perform retrieval."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip install fastembed --quiet --upgrade"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Importing the necessary libraries:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from fastembed import TextEmbedding"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Preparation\n",
    "We initialize the embedding model and generate embeddings for the documents.\n",
    "\n",
    "### 💡 Tip: Prefer using `query_embed` for queries and `passage_embed` for documents."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(384,) 10\n"
     ]
    }
   ],
   "source": [
    "# Example list of documents\n",
    "documents: list[str] = [\n",
    "    \"Maharana Pratap was a Rajput warrior king from Mewar\",\n",
    "    \"He fought against the Mughal Empire led by Akbar\",\n",
    "    \"The Battle of Haldighati in 1576 was his most famous battle\",\n",
    "    \"He refused to submit to Akbar and continued guerrilla warfare\",\n",
    "    \"His capital was Chittorgarh, which he lost to the Mughals\",\n",
    "    \"He died in 1597 at the age of 57\",\n",
    "    \"Maharana Pratap is considered a symbol of Rajput resistance against foreign rule\",\n",
    "    \"His legacy is celebrated in Rajasthan through festivals and monuments\",\n",
    "    \"He had 11 wives and 17 sons, including Amar Singh I who succeeded him as ruler of Mewar\",\n",
    "    \"His life has been depicted in various films, TV shows, and books\",\n",
    "]\n",
    "# Initialize the DefaultEmbedding class with the desired parameters\n",
    "embedding_model = TextEmbedding(model_name=\"BAAI/bge-small-en\")\n",
    "\n",
    "# We'll use the passage_embed method to get the embeddings for the documents\n",
    "embeddings: list[np.ndarray] = list(\n",
    "    embedding_model.passage_embed(documents)\n",
    ")  # notice that we are casting the generator to a list\n",
    "\n",
    "print(embeddings[0].shape, len(embeddings))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Querying\n",
    "\n",
    "We'll define a function to print the top k documents based on a query, and prepare a sample query."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"Who was Maharana Pratap?\"\n",
    "query_embedding = list(embedding_model.query_embed(query))[0]\n",
    "plain_query_embedding = list(embedding_model.embed(query))[0]\n",
    "\n",
    "\n",
    "def print_top_k(query_embedding, embeddings, documents, k=5):\n",
    "    # use numpy to calculate the cosine similarity between the query and the documents\n",
    "    scores = np.dot(embeddings, query_embedding)\n",
    "    # sort the scores in descending order\n",
    "    sorted_scores = np.argsort(scores)[::-1]\n",
    "    # print the top 5\n",
    "    for i in range(k):\n",
    "        print(f\"Rank {i+1}: {documents[sorted_scores[i]]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([-0.06002192,  0.04322132, -0.00545516, -0.04419701, -0.00542277],\n",
       "       dtype=float32),\n",
       " array([-0.06002192,  0.04322132, -0.00545516, -0.04419701, -0.00542277],\n",
       "       dtype=float32))"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "query_embedding[:5], plain_query_embedding[:5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `query_embed` is specifically designed for queries, leading to more relevant and context-aware results. The retrieved documents tend to align closely with the query's intent.\n",
    "\n",
    "In contrast, `embed` is a more general-purpose representation that might not capture the nuances of the query as effectively. The retrieved documents using plain embeddings might be less relevant or ordered differently compared to the results obtained using query embeddings.\n",
    "\n",
    "Conclusion: Using query and passage embeddings leads to more relevant and context-aware results."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "fst",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
